﻿using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using MemoryPack;
#if UNITY_EDITOR
using Sirenix.OdinInspector;
#endif

namespace Hsenl {
    // 只有一个子节点, 放在某个子节点上面, 用于修饰该子节点
    [Serializable]
    [MemoryPackable(GenerateType.NoGenerate)]
    public abstract partial class DecoratorNode<TManager, TNode> : Node<TManager>, IBreadNode<TNode>
        where TManager : class, IBehaviorTree
        where TNode : class, INode {
#if UNITY_EDITOR
        [ShowInInspector]
#endif
        [MemoryPackInclude]
        protected List<INode> childone = new(1) { null };

        protected INode Child {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get => this.childone[0];
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            set => this.childone[0] = value;
        }

        [MemoryPackOnSerialized]
        private void OnMemoryPackSerialized() {
            if (this.childone == null) return;
            SetParent_Internal(this.Child, this);
        }

        public sealed override void AwakeNode(IBehaviorTree tree) {
            base.AwakeNode(tree);
            this.Child?.AwakeNode(tree);
        }

        public sealed override void EnableNode() {
            base.EnableNode();
            this.Child?.EnableNode();
        }

        public sealed override void DisableNode() {
            base.DisableNode();
            this.Child?.DisableNode();
        }

        public sealed override void DestroyNode() {
            base.DestroyNode();
            this.Child?.DestroyNode();
        }

        public sealed override void ResetNode() {
            base.ResetNode();
            this.Child?.ResetNode();
        }

        public sealed override void AbortNode() {
            base.AbortNode();
            this.Child?.AbortNode();
        }

        public sealed override bool EvaluateNode() {
            return base.EvaluateNode();
        }

        public sealed override NodeStatus TickNode() {
            return base.TickNode();
        }

        public sealed override void StartRun() {
            base.StartRun();
        }

        public sealed override void Run() {
            base.Run();
        }

        public sealed override void StopRun() {
            base.StopRun();
        }

        public bool AddChild(TNode node) {
            if (this.manager != null)
                throw new InvalidOperationException("Can't modified nodes when node is awaked.");

            if (node == null)
                throw new ArgumentNullException();

            if (this.Child != null) return false;
            this.Child = node;
            SetParent_Internal(node, this);
            node.ForeachChildren(SetParent_Internal, this);

            return true;
        }

        public bool RemoveChild(TNode node) {
            if (this.manager != null)
                throw new InvalidOperationException("Can't modified nodes when node is awaked.");

            if (node == null)
                throw new ArgumentNullException();

            if (this.Child != node)
                return false;

            this.Child = null;
            SetParent_Internal(node, null);
            node.ForeachChildren(SetParent_Internal, default(TNode));

            return true;
        }

        public void Clear() {
            if (this.manager != null)
                throw new InvalidOperationException("Can't modified nodes when node is awaked.");

            if (this.Child != null)
                this.RemoveChild((TNode)this.Child);
        }

        public sealed override IteratorList<INode> ForeachChildrenExcludeGrandchild() {
            if (this.Child == null)
                return default;

            return new IteratorList<INode>(this.childone);
        }

        public override void ForeachChildren<T>(Action<INode, T> action, T userData = default) {
            if (this.Child == null) return;

            action.Invoke(this.Child, userData);

            this.Child.ForeachChildren(action, userData);
        }

        public sealed override T GetNodeInChildren<T>() {
            if (this.Child == null) return default;

            if (this.Child is T t) {
                return t;
            }

            return this.Child.GetNodeInChildren<T>();
        }

        public sealed override T[] GetNodesInChildren<T>() {
            if (this.Child == null)
                return null;

            using var list = ListComponent<T>.Rent();
            this.GetNodesInChildren(list);
            return list.ToArray();
        }

        public sealed override void GetNodesInChildren<T>(List<T> cache) {
            if (this.Child == null) return;
            if (this.Child is T t) {
                cache.Add(t);
            }

            this.Child.GetNodesInChildren(cache);
        }
    }
}